import json
import logging
import requests
from django.http import JsonResponse
from ..models import Problem
from .load_balance import LoadBalance

logger = logging.getLogger(__name__)


class Control:
    def __init__(self):
        """初始化控制模块，包括模型、视图和负载均衡器"""
        self.load_balance = LoadBalance()
        logger.info("判题控制模块初始化完成")

    def all_problems(self):
        """获取所有题目信息"""
        try:
            problems = Problem.objects.all().order_by('id')
            return list(problems.values())
        except Exception as e:
            logger.error(f"获取题目列表失败: {str(e)}")
            return []

    def problem(self, problem_id):
        """获取指定题目信息"""
        try:
            problem = Problem.objects.get(id=problem_id)
            return {
                'id': problem.id,
                'title': problem.title,
                'star': problem.star,
                'desc': problem.desc,
                'header': problem.header,
                'tail': problem.tail,
                'cpu_limit': problem.cpu_limit,
                'mem_limit': problem.mem_limit
            }
        except Problem.DoesNotExist:
            logger.warning(f"题目ID[{problem_id}]不存在")
            return None
        except Exception as e:
            logger.error(f"获取题目ID[{problem_id}]信息失败: {str(e)}")
            return None

    def judge(self, problem_id, request_data):
        """
        负载均衡判题
        request_data: 包含用户代码和输入的字典
        """
        # 获取题目信息
        problem = self.problem(problem_id)
        if not problem:
            return JsonResponse({'error': f'题目ID[{problem_id}]不存在'})

        # 拼接用户代码和测试代码
        user_code = request_data.get('code', '')
        test_code = problem.get('tail', '')
        full_code = user_code + '\n' + test_code

        # 准备编译请求数据
        compile_data = {
            'code': full_code,
            'input': request_data.get('input', ''),
            'cpu_limit': problem.get('cpu_limit', 1000),
            'mem_limit': problem.get('mem_limit', 256)
        }

        # 选择负载最低的主机
        machine_id, machine = self.load_balance.select_machine()
        if not machine:
            return JsonResponse({'error': '没有可用的判题主机'})

        machine.inc_load()  # 主机负载加1
        logger.info(f"选择主机[{machine_id}]: {machine.ip}:{machine.port}, 当前负载: {machine.get_load()}")

        try:
            # 发送判题请求
            url = f"http://{machine.ip}:{machine.port}/compile_and_run"
            response = requests.post(
                url,
                data=json.dumps(compile_data),
                headers={'Content-Type': 'application/json;charset=UTF-8'},
                timeout=10
            )

            machine.dec_load()  # 处理完成，负载减1

            if response.status_code == 200:
                # 判题成功，返回结果
                result = response.json()
                logger.info(f"判题成功，结果: {result.get('status')}")
                return JsonResponse(result)
            else:
                # 判题服务返回错误
                machine.offline_machine(machine_id)  # 标记主机离线
                logger.error(f"判题服务返回错误状态码: {response.status_code}")
                return JsonResponse({
                    'error': '判题服务错误',
                    'status_code': response.status_code
                })

        except requests.ConnectionError:
            # 连接主机失败
            machine.dec_load()  # 负载减1
            machine.offline_machine(machine_id)  # 标记主机离线
            logger.error(f"无法连接到主机 {machine.ip}:{machine.port}，已标记为离线")
            self.load_balance.show_machines()  # 显示当前主机状态

            # 尝试重新上线所有主机
            self.load_balance.online_machine()
            return JsonResponse({'error': '判题主机连接失败，请稍后再试'})
        except Exception as e:
            # 其他异常
            machine.dec_load()  # 负载减1
            logger.error(f"判题过程发生异常: {str(e)}")
            return JsonResponse({'error': f'判题异常: {str(e)}'})